namespace OneWorks.Utils.FluentData
{
    using System;
    using System.Collections.Generic;
    using System.Dynamic;
    using System.Linq;
    using System.Linq.Expressions;

    internal class ActionsHandler
    {
        private readonly BuilderData _data;

        internal ActionsHandler(BuilderData data)
        {
            this._data = data;
        }

        internal void ColumnValueAction(string columnName, object value, DataTypes parameterType, int size)
        {
            this.ColumnAction(columnName, value, typeof(object), parameterType, size);
        }

        private void ColumnAction(string columnName, object value, Type type, DataTypes parameterType, int size)
        {
            var parameterName = columnName;

            this._data.Columns.Add(new BuilderColumn(columnName, value, parameterName));

            if(parameterType == DataTypes.Object)
                parameterType = this._data.Command.Data.Context.Data.Provider.GetDbTypeForClrType(type);

            this.ParameterAction(parameterName, value, parameterType, ParameterDirection.Input, size);
        }

        internal void ColumnValueAction<T>(Expression<Func<T, object>> expression, DataTypes parameterType, int size)
        {
            var parser = new PropertyExpressionParser<T>(this._data.Item, expression);

            this.ColumnAction(parser.Name, parser.Value, parser.Type, parameterType, size);
        }

        internal void ColumnValueDynamic(ExpandoObject item, string propertyName, DataTypes parameterType, int size)
        {
            var propertyValue = (item as IDictionary<string, object>) [propertyName];

            this.ColumnAction(propertyName, propertyValue, typeof(object), parameterType, size);
        }

        internal void AutoMapColumnsAction<T>(params Expression<Func<T, object>>[] ignorePropertyExpressions)
        {
            var properties = ReflectionHelper.GetProperties(this._data.Item.GetType());
            var ignorePropertyNames = new HashSet<string>();
            if (ignorePropertyExpressions != null)
            {
                foreach (var ignorePropertyExpression in ignorePropertyExpressions)
                {
                    var ignorePropertyName = new PropertyExpressionParser<T>(this._data.Item, ignorePropertyExpression).Name;
                    ignorePropertyNames.Add(ignorePropertyName);
                }
            }

            foreach (var property in properties)
            {
                var ignoreProperty = ignorePropertyNames.SingleOrDefault(x => x.Equals(property.Value.Name, StringComparison.CurrentCultureIgnoreCase));
                if (ignoreProperty != null)
                    continue;

                var propertyType = ReflectionHelper.GetPropertyType(property.Value);

                var propertyValue = ReflectionHelper.GetPropertyValue(this._data.Item, property.Value);
                this.ColumnAction(property.Value.Name, propertyValue, propertyType, DataTypes.Object, 0);
            }
        }

        internal void AutoMapDynamicTypeColumnsAction(params string[] ignorePropertyExpressions)
        {
            var properties = (IDictionary<string, object>) this._data.Item;
            var ignorePropertyNames = new HashSet<string>();
            if (ignorePropertyExpressions != null)
            {
                foreach (var ignorePropertyExpression in ignorePropertyExpressions)
                    ignorePropertyNames.Add(ignorePropertyExpression);
            }

            foreach (var property in properties)
            {
                var ignoreProperty = ignorePropertyNames.SingleOrDefault(x => x.Equals(property.Key, StringComparison.CurrentCultureIgnoreCase));

                if (ignoreProperty == null)
                    this.ColumnAction(property.Key, property.Value, typeof(object), DataTypes.Object, 0);
            }
        }

        private void ParameterAction(string name, object value, DataTypes dataType, ParameterDirection direction, int size)
        {
            this._data.Command.Parameter(name, value, dataType, direction, size);
        }

        internal void ParameterOutputAction(string name, DataTypes dataTypes, int size)
        {
            this.ParameterAction(name, null, dataTypes, ParameterDirection.Output, size);
        }

        internal void WhereAction(string columnName, object value, DataTypes parameterType, int size)
        {
            var parameterName = columnName;
            this.ParameterAction(parameterName, value, parameterType, ParameterDirection.Input, 0);

            this._data.Where.Add(new BuilderColumn(columnName, value, parameterName));
        }

        internal void WhereAction<T>(Expression<Func<T, object>> expression, DataTypes parameterType, int size)
        {
            var parser = new PropertyExpressionParser<T>(this._data.Item, expression);
            this.WhereAction(parser.Name, parser.Value, parameterType, size);
        }
    }
}